package com.cnap.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.cnap.db.entity.*;
import com.cnap.db.mapper.ScheduleStrategyMapper;
import com.cnap.db.mapper.StageMapper;
import com.cnap.db.mapper.StrategyMapper;
import com.cnap.model.application.ApplicationStatusEnum;
import com.cnap.model.cloud.CloudVendorsEnum;
import com.cnap.model.cloud.CloudVmModel;
import com.cnap.model.cloud.CommentResultForCloud;
import com.cnap.model.cloud.CreateCloudParam;
import com.cnap.model.cloud.scaling.CreateScalingDTO;
import com.cnap.model.cloud.scaling.ScalingPolicyDTO;
import com.cnap.model.cloud.scaling.UpdateScalingParam;
import com.cnap.model.exception.JsonException;
import com.cnap.model.intelligent.IntelligentConstants;
import com.cnap.model.schedule.DistributeParam;
import com.cnap.model.schedule.MicroServiceVO;
import com.cnap.model.schedule.StrategyStatusEnum;
import com.cnap.model.schedule.UpdateMicroServiceStatusParam;
import com.cnap.model.schedule.ValidateParam;
import com.cnap.model.schedule.engine.EngineClusterInfo;
import com.cnap.model.schedule.engine.VmModel;
import com.cnap.model.schedule.engine.alg.AlgDeployClusterInfo;
import com.cnap.model.schedule.engine.alg.ClusterDetail;
import com.cnap.model.schedule.engine.alg.ResourceTypeEnum;
import com.cnap.model.stage.*;
import com.cnap.model.strategy.DistributeParamV2;
import com.cnap.model.strategy.schedule.SvcTypeEnum;
import com.cnap.service.*;
import com.cnap.utils.JsonUtil;
import com.cnap.utils.TokenUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * <类文件描述>
 *
 * @author xc
 **/
@Service
public class DistributeDeployServiceImpl implements DistributeDeployService {
    private static final Logger LOGGER = LoggerFactory.getLogger(DistributeDeployServiceImpl.class);

    private final StageMapper stageMapper;
    private final CloudMgmtService cloudMgmtService;
    private final MicroSvcService microSvcService;
    private final ClusterInfoService clusterInfoService;
    private final StrategyMapper strategyMapper;
    private final ScheduleStrategyMapper scheduleStrategyMapper;
    private final StageService stageService;

    @Autowired
    public DistributeDeployServiceImpl(StageMapper stageMapper, CloudMgmtService cloudMgmtService,
                                       MicroSvcService microSvcService,
                                       ClusterInfoService clusterInfoService, StrategyMapper strategyMapper,
                                       ScheduleStrategyMapper scheduleStrategyMapper, StageService stageService) {
        this.stageMapper = stageMapper;
        this.cloudMgmtService = cloudMgmtService;
        this.microSvcService = microSvcService;
        this.clusterInfoService = clusterInfoService;
        this.strategyMapper = strategyMapper;
        this.scheduleStrategyMapper = scheduleStrategyMapper;
        this.stageService = stageService;
    }

    /**
     * 分部署部署
     *
     * @param deployParam 部署参数
     */
    @Override
    // 废弃
    public void startDistributeDeploy(StageDeployParam deployParam) {
        List<SingleStageDeployParam> params = deployParam.getParams();
        // 更新本地同一个集群下，其他微服务的虚机扩缩容策略
        if (!params.isEmpty()) {
            SingleStageDeployParam lastParam = params.get(params.size() - 1);
            LOGGER.info("update local vm strategy, use serviceId={}", lastParam.getSvcId());
            StrategyPO vmStrategy = strategyMapper.selectVmScaleByServiceId(lastParam.getSvcId());
            updateOtherServiceLocalVmStrategy(vmStrategy);
        }

        for (SingleStageDeployParam param : params) {
            // 0. 判断微服务是否存在
            // 判断是否存在微服务
            String svcId = param.getSvcId();
            MicroServicePO s = microSvcService.getMicroserviceBySvcId(svcId);
            if (Objects.isNull(s)) {
                LOGGER.error("can't find micro service, id={}", svcId);
                throw new JsonException(404, "未找到该微服务");
            }

            ClusterInfoPO clusterInfoPO = clusterInfoService.getClusterInfoBySvcId(param.getSvcId());
            List<AlgDeployClusterInfo> createdAlgDeployInfos =
                    clusterInfoPO.getAlgDeployClusterInfos().stream().filter(algDeployClusterInfo -> ResourceTypeEnum.CREATE.getType().equals(algDeployClusterInfo.getResourceType())).collect(Collectors.toList());

            List<ClusterDetail> clusterDetails = new ArrayList<>();


            for (AlgDeployClusterInfo algDeployClusterInfo : createdAlgDeployInfos) {
                clusterDetails.addAll(algDeployClusterInfo.getClusterDetails());
            }
            // 需要过滤掉非用户选择的云商
            List<VmModel> vmModels = clusterDetails.stream()
                    .filter(clusterDetail -> clusterInfoPO.getUserChooseCloud().equals(clusterDetail.getCloudType()))
                    .map(ClusterDetail::toVmModel).collect(Collectors.toList());
            List<EngineClusterInfo> clusterInfos = clusterInfoPO.getClusterInfos();

            // 非空才需要去创建
            if (!clusterDetails.isEmpty()) {
                // 1. 云资源
                // 2. 安全组端口
                // 3. k8s纳管
                // 1 2 3步骤统一对接多云管理
                // 由多云管理回调3次，更新步骤状态
                // 生成多云管理创建云资源参数
                // 多云管理系统是异步，所以此处可以使用同步
                CreateCloudParam createCloudParam = new CreateCloudParam();
                List<CloudVmModel> cloudVmModels = vmModels.stream().map(VmModel::toCloudVmModel).collect(Collectors.toList());
                createCloudParam.setVmModels(cloudVmModels);
                createCloudParam.setSvcId(param.getSvcId());
                createCloudParam.setType(StageTypeEnum.CLOUD_RESOURCE.getType());
                createCloudParam.setUserName(TokenUtils.getUserName());
                createCloudParam.setCategory(s.getCategory());
                createCloudParam.setAppId(s.getApplicationId());

                CreateScalingDTO createScalingDTO = getScalingCreateDTO(param, s, clusterInfoPO);

                CommentResultForCloud result = cloudMgmtService.createCloudResource(createCloudParam);
                LOGGER.info("get result from multi system, code={}", result.getCode());
                // 4. 部署
                // 默认点击后，全自动部署
                // 此处更新服务状态为启动中，但页面无法删除，会提示在启动中
                // 更新服务状态为启动中
                UpdateMicroServiceStatusParam updateMicroServiceStatusParam = new UpdateMicroServiceStatusParam();
                updateMicroServiceStatusParam.setId(svcId);
                updateMicroServiceStatusParam.setStatus(String.valueOf(StrategyStatusEnum.STARTING.getStatus()));
                microSvcService.updateMicroServiceStatus(updateMicroServiceStatusParam);
            } else {
                // 发送最新的策略到集群控制台
                CreateScalingDTO createScalingDTO = getScalingCreateDTO(param, s, clusterInfoPO);
                if (Objects.nonNull(createScalingDTO)) {
                    List<String> clusterNames = clusterInfos.stream().map(EngineClusterInfo::getName).collect(Collectors.toList());
                    // 应该只有一个集群
                    createScalingDTO.setClusterNames(clusterNames.get(0));
                    createScalingDTO.setProvider(CloudVendorsEnum.changeFromCloudTypeToDescribe(clusterInfos.get(0).getProviderName()));

                    CommentResultForCloud result = cloudMgmtService.insertNewScalingPolicy(Collections.singletonList(createScalingDTO));
                    LOGGER.info("get result from multi system, code={}", result.getCode());
                }

                List<String> clusterNames = clusterInfos.stream().map(EngineClusterInfo::getName).collect(Collectors.toList());
                ValidateParam validateParam = new ValidateParam();
                validateParam.setServiceId(param.getSvcId());
                validateParam.setClusters(clusterNames);
                // 使用数据库中的yaml
                validateParam.setYaml("");

                microSvcService.validate(validateParam);

                DistributeParam distributeParam = new DistributeParam();
                distributeParam.setServiceId(param.getSvcId());
                distributeParam.setClusters(new ArrayList<>(clusterNames));
                distributeParam.setYaml("yaml");
                microSvcService.distribute(distributeParam);
                // 没问题的情况下，需要手动创建前3步成功的数据
                createSuccessStages(param.getSvcId());
            }
        }
    }

    @Override
    public void startDistributeDeploy(List<String> svcIds) {
        for (String svcId : svcIds) {
            // 0. 判断微服务是否存在
            // 判断是否存在微服务
            MicroServicePO s = microSvcService.getMicroserviceBySvcId(svcId);
            if (Objects.isNull(s)) {
                LOGGER.error("can't find micro service, id={}", svcId);
                throw new JsonException(404, "未找到该微服务");
            }
            ApplicationService applicationService = SpringUtil.getBean(ApplicationService.class);
            ApplicationPO applicationPO = applicationService.selectByAppId(s.getApplicationId());

            ScheduleStrategyPO scheduleStrategyPO = scheduleStrategyMapper.selectByServiceId(svcId);
            String userChooseAccountName = scheduleStrategyPO.getUserChooseAccountName();

            // 需要使用用户选择的云商
            List<AlgDeployClusterInfo> algDeployClusterInfos = scheduleStrategyPO.getAlgDeployClusterInfos().stream()
                    .filter(algDeployClusterInfo -> algDeployClusterInfo.getProviderName().equals(scheduleStrategyPO.getUserChooseCloud())
                            && algDeployClusterInfo.getAccountName().equals(userChooseAccountName))
                    .collect(Collectors.toList());
            // 过滤完全新建或者添加的情况
            List<AlgDeployClusterInfo> needCreateInfos = algDeployClusterInfos.stream()
                    .filter(algDeployClusterInfo -> ResourceTypeEnum.CREATE.getType().equals(algDeployClusterInfo.getResourceType())
                            || ResourceTypeEnum.ADD.getType().equals(algDeployClusterInfo.getResourceType())).collect(Collectors.toList());
            List<ClusterDetail> clusterDetails = needCreateInfos.stream()
                    .map(AlgDeployClusterInfo::getClusterDetails)
                    .flatMap(Collection::stream)
                    .collect(Collectors.toList());

            List<VmModel> vmModels = clusterDetails.stream()
                    .filter(clusterDetail -> scheduleStrategyPO.getUserChooseCloud().equals(clusterDetail.getCloudType()))
                    .map(ClusterDetail::toVmModel).collect(Collectors.toList());

            String arch = scheduleStrategyPO.checkArch();
            String cloudScope = applicationPO.getDeployResourceInfo().getCloudScope();
            // 非空才需要去创建
            if (!clusterDetails.isEmpty()) {
                // 1. 云资源
                // 2. 安全组端口
                // 3. k8s纳管
                // 1 2 3步骤统一对接多云管理
                // 由多云管理回调3次，更新步骤状态
                // 生成多云管理创建云资源参数
                // 多云管理系统是异步，所以此处可以使用同步
                CreateCloudParam createCloudParam = getCreateCloudParam(svcId, s, vmModels, userChooseAccountName, arch, cloudScope);

                CommentResultForCloud result = cloudMgmtService.createCloudResource(createCloudParam);
                LOGGER.info("get result from multi system, code={}", result.getCode());
                // 4. 部署
                // 默认点击后，全自动部署
                // 此处更新服务状态为启动中，但页面无法删除，会提示在启动中
                // 更新服务状态为启动中
                UpdateMicroServiceStatusParam updateMicroServiceStatusParam = new UpdateMicroServiceStatusParam();
                updateMicroServiceStatusParam.setId(svcId);
                updateMicroServiceStatusParam.setStatus(String.valueOf(StrategyStatusEnum.STARTING.getStatus()));
                microSvcService.updateMicroServiceStatus(updateMicroServiceStatusParam);

                applicationService.updateApplicationStatus(s.getApplicationId(), ApplicationStatusEnum.IN_DISTRIBUTE.getValue());
            } else {
                // 已有集群满足要求，直接下发部署
                List<AlgDeployClusterInfo> existInfo = algDeployClusterInfos.stream()
                        .filter(algDeployClusterInfo -> ResourceTypeEnum.EXIST.getType().equals(algDeployClusterInfo.getResourceType()))
                        .collect(Collectors.toList());

                List<String> clusterNames = existInfo.stream().map(AlgDeployClusterInfo::getName)
                        .filter(Objects::nonNull) // 过滤集群名称为空的情况
                        .collect(Collectors.toList());
                ValidateParam validateParam = new ValidateParam();
                validateParam.setServiceId(svcId);
                validateParam.setClusters(clusterNames);
                // 使用数据库中的yaml
                validateParam.setYaml("");

                microSvcService.validate(validateParam);

                OperateService operateService = SpringUtil.getBean(OperateService.class);

                List<DeployClusterPO> clusterPOS = existInfo.stream()
                        .filter(algDeployClusterInfo -> Objects.nonNull(algDeployClusterInfo.getName())) // 过滤集群名称为空的情况
                        .map(algDeployClusterInfo -> {
                            DeployClusterPO po = algDeployClusterInfo.toDeployClusterPo();
                            po.setServiceId(svcId);
                            return po;
                        }).collect(Collectors.toList());

                UpdateMicroServiceStatusParam updateMicroServiceStatusParam = new UpdateMicroServiceStatusParam();
                updateMicroServiceStatusParam.setId(svcId);
                updateMicroServiceStatusParam.setStatus(String.valueOf(StrategyStatusEnum.STARTING.getStatus()));
                microSvcService.updateMicroServiceStatus(updateMicroServiceStatusParam);

                DistributeParamV2 distributeParam = new DistributeParamV2();
                distributeParam.setServiceId(svcId);
                distributeParam.setClusters(clusterPOS);
                // 发送部署请求
                operateService.distributeV2(distributeParam);
                // 没问题的情况下，需要手动创建前3步成功的数据
                createSuccessStages(svcId);
                applicationService.updateApplicationStatus(s.getApplicationId(), ApplicationStatusEnum.IN_DISTRIBUTE.getValue());
            }

        }
    }

    private static CreateCloudParam getCreateCloudParam(String svcId, MicroServicePO s, List<VmModel> vmModels,
                                                        String userChooseAccountName, String arch, String cloudScope) {
        CreateCloudParam createCloudParam = new CreateCloudParam();
        List<CloudVmModel> cloudVmModels = vmModels.stream().map(VmModel::toCloudVmModel).collect(Collectors.toList());
        createCloudParam.setVmModels(cloudVmModels);
        createCloudParam.setSvcId(svcId);
        createCloudParam.setSvcName(s.getServiceName());
        createCloudParam.setType(StageTypeEnum.CLOUD_RESOURCE.getType());
        createCloudParam.setUserName(TokenUtils.getUserName());
        createCloudParam.setCategory(s.getCategory());
        createCloudParam.setAppId(s.getApplicationId());
        createCloudParam.setAccountName(userChooseAccountName);
        createCloudParam.setArch(arch);
        createCloudParam.setCloudScope(cloudScope);
        return createCloudParam;
    }

    /**
     * 本地其他微服务如果部署在同一个集群上，更新其虚机扩缩容策略
     *
     * @param scaleStrategy 虚机扩缩容策略
     */
    private void updateOtherServiceLocalVmStrategy(StrategyPO scaleStrategy) {
        MicroServicePO microServicePO = microSvcService.getMicroserviceBySvcId(scaleStrategy.getServiceId());
        ClusterInfoPO clusterInfoPO = clusterInfoService.getClusterInfoBySvcId(scaleStrategy.getServiceId());
        if (Objects.isNull(clusterInfoPO)) {
            LOGGER.error("current service has no cluster info, svcId={}", scaleStrategy.getServiceId());
            return;
        }
        List<EngineClusterInfo> engineClusterInfos = clusterInfoPO.getClusterInfos().stream()
                .filter(info -> clusterInfoPO.getUserChooseCloud().equals(info.getProviderName()))
                .filter(info -> Objects.nonNull(info.getName())).collect(Collectors.toList());
        if (engineClusterInfos.isEmpty()) {
            LOGGER.warn("current service has no exit cluster, svcId={}", scaleStrategy.getServiceId());
            return;
        }
        UpdateScalingParam updateScalingParam = new UpdateScalingParam();
        updateScalingParam.setProvider(clusterInfoPO.getUserChooseCloud());
        updateScalingParam.setClusterName(engineClusterInfos.get(0).getName());
        ScalingPolicyDTO scalingPolicyDTO = JsonUtil.jsonStrToObject(scaleStrategy.getStrategyParam(),
                ScalingPolicyDTO.class);
        if (Objects.isNull(scalingPolicyDTO)) {
            updateScalingParam.setScalingPolicyPOS(null);
        } else {
            updateScalingParam.setScalingPolicyPOS(scalingPolicyDTO.getParam());
        }
        updateScalingParam.setCategory(microServicePO.getCategory());
        MicroSvcService microSvcService = SpringUtil.getBean(MicroSvcService.class);
        microSvcService.updateScalingPolicy(updateScalingParam);
    }

    private CreateScalingDTO getScalingCreateDTO(SingleStageDeployParam param, MicroServicePO s, ClusterInfoPO clusterInfoPO) {
        CreateScalingDTO createScalingDTO = new CreateScalingDTO();
        createScalingDTO.setScalingLabel(s.getId());
        EngineClusterInfo engineClusterInfo = clusterInfoPO.getClusterInfos().stream()
                .filter(info -> clusterInfoPO.getUserChooseCloud().equals(info.getProviderName()))
                .filter(info -> Objects.nonNull(info.getName())).findFirst().orElse(null);
        if (Objects.nonNull(engineClusterInfo)) {
            createScalingDTO.setClusterNames(engineClusterInfo.getName());
            createScalingDTO.setProvider(CloudVendorsEnum.changeFromCloudTypeToDescribe(engineClusterInfo.getProviderName()));
        }

        StrategyPO vmStrategy = strategyMapper.selectVmScaleByServiceId(param.getSvcId());
        if (Objects.nonNull(vmStrategy)) {
            ScalingPolicyDTO scalingPolicyDTO = JsonUtil.jsonStrToObject(vmStrategy.getStrategyParam(),
                    ScalingPolicyDTO.class);
            if (Objects.nonNull(scalingPolicyDTO) && !"none".equals(scalingPolicyDTO.getPolicy())) {
                createScalingDTO.setScalingPolicyPOs(scalingPolicyDTO.getParam());
            } else {
                // 删除策略
                // 对方系统支持null， 判断null则删除策略
                createScalingDTO.setScalingPolicyPOs(null);
            }
        } else {
            // 删除策略
            // 对方系统支持null， 判断null则删除策略
            createScalingDTO.setScalingPolicyPOs(null);
        }
        return createScalingDTO;
    }

    private void createSuccessStages(String serviceId) {
        MicroServicePO microServicePO = microSvcService.getMicroserviceBySvcId(serviceId);
        ApplicationService applicationService = SpringUtil.getBean(ApplicationService.class);
        ApplicationPO applicationPO = applicationService.selectByAppId(microServicePO.getApplicationId());
        ScheduleStrategyPO scheduleStrategyPO = scheduleStrategyMapper.selectByServiceId(serviceId);

        String cloutType = applicationPO.getDeployResourceInfo().getCloudScope();
        String svcType = scheduleStrategyPO.getBusinessScene().getSvcType();
        SvcTypeEnum svcTypeEnum = SvcTypeEnum.getSvcTypeEnumBySvcType(svcType);
        assert svcTypeEnum != null;
        String arch = svcTypeEnum.getDetailType();

        List<StageEnum2> currentTypeStageEnums = StageEnum2.getStageEnum2WithParam(cloutType, arch);

        // 创建全量阶段信息

        for (StageEnum2 detailTypeEnum : currentTypeStageEnums) {
            // 跳过未知的
            if (StageEnum2.UNKNOWN.equals(detailTypeEnum)) {
                continue;
            }
            // 跳过步骤4,
            // 步骤4在部署下去后，当状态改变时进行创建
            if (StageTypeEnum.DISTRIBUTE.getType().equals(detailTypeEnum.getParentType())) {
                continue;
            }
            StagePO stagePO = new StagePO();
            // detailType
            stagePO.setDetailType(detailTypeEnum.getDetailType());
            // order
            stagePO.setOrder(detailTypeEnum.getOrder());
            // id
            stagePO.setId(IdUtil.simpleUUID());
            // svcId
            stagePO.setServiceId(serviceId);
            // type
            stagePO.setType(detailTypeEnum.getParentType());
            // cloutType
            stagePO.setCloudScope(detailTypeEnum.getCloudScope());
            // arch
            stagePO.setArch(detailTypeEnum.getArch());

            stageMapper.insertStage(stagePO);
        }
    }

    /**
     * 根据服务id查询阶段信息
     *
     * @param svcId 微服务信息
     * @return 结果
     */
    @Override
    public StageVO getStageInfoByServiceId(String svcId) {
        // 第一阶段
        List<StagePO> firstStage = stageMapper.selectStageByServiceIdAndType(svcId, StageTypeEnum.CLOUD_RESOURCE.getType());
        // 第二阶段
        List<StagePO> secondStage = stageMapper.selectStageByServiceIdAndType(svcId, StageTypeEnum.SAFE_PORT.getType());
        // 第三阶段
        List<StagePO> thirdStage = stageMapper.selectStageByServiceIdAndType(svcId, StageTypeEnum.K8S.getType());
        // 第四阶段
        List<StagePO> fourthStage = stageMapper.selectStageByServiceIdAndType(svcId, StageTypeEnum.DISTRIBUTE.getType());

        StageVO stageVO = new StageVO();
        // svcId
        stageVO.setSvcId(svcId);
        // 默认给空数组
        // firstStage
        stageVO.setFirstStages(Optional.ofNullable(firstStage).orElse(Collections.emptyList()).stream().map(SingleStageVO::fromPO).collect(Collectors.toList()));
        // 默认给空数组
        // secondStage
        stageVO.setSecondStages(Optional.ofNullable(secondStage).orElse(Collections.emptyList()).stream().map(SingleStageVO::fromPO).collect(Collectors.toList()));
        // 默认给空数组
        // thirdStage
        stageVO.setThirdStages(Optional.ofNullable(thirdStage).orElse(Collections.emptyList()).stream().map(SingleStageVO::fromPO).collect(Collectors.toList()));
        // 默认给空数组
        // fourthStage
        stageVO.setFourthStages(Optional.ofNullable(fourthStage).orElse(Collections.emptyList()).stream().map(SingleStageVO::fromPO).collect(Collectors.toList()));

        return stageVO;
    }

    /**
     * 根据应用id查询该应用下微服务的阶段信息
     *
     * @param appId 应用id
     * @return 结果
     */
    @Override
    public List<StageVO> getStagesInfoByAppId(String appId) {
        List<MicroServiceVO> microServiceVOS = microSvcService.selectMicroServicesByAppId(appId);
        return microServiceVOS.stream().map(po -> getStageInfoByServiceId(po.getId())).collect(Collectors.toList());
    }

    /**
     * 根据服务id重试
     *
     * @param svcId svcId
     */
    @Override
    public void retryByServiceId(String svcId) {
        MicroServicePO microServicePO = microSvcService.getMicroserviceBySvcId(svcId);
        ScheduleStrategyPO scheduleStrategyPO = scheduleStrategyMapper.selectByServiceId(svcId);
        String userChooseAccountName = scheduleStrategyPO.getUserChooseAccountName();
        String arch = scheduleStrategyPO.checkArch();

        ApplicationService applicationService = SpringUtil.getBean(ApplicationService.class);
        ApplicationPO applicationPO = applicationService.selectByAppId(microServicePO.getApplicationId());
        String cloudScope = applicationPO.getDeployResourceInfo().getCloudScope();

        // 判断当前在哪个阶段失败
        List<StagePO> cloudStage = stageMapper.selectStageByServiceIdAndType(svcId, StageTypeEnum.CLOUD_RESOURCE.getType());
        if (CollUtil.isNotEmpty(cloudStage) && checkError(cloudStage)) {
            // 重试云资源开通
            CreateCloudParam createCloudParam = new CreateCloudParam();
            createCloudParam.setSvcId(svcId);
            createCloudParam.setType(StageTypeEnum.CLOUD_RESOURCE.getType());
            createCloudParam.setUserName(TokenUtils.getUserName());
            createCloudParam.setCategory(microServicePO.getCategory());
            createCloudParam.setAppId(microServicePO.getApplicationId());
            createCloudParam.setArch(arch);
            createCloudParam.setAccountName(userChooseAccountName);
            createCloudParam.setCloudScope(cloudScope);
            cloudMgmtService.createCloudResource(createCloudParam);

            stageMapper.deleteStageByIds(cloudStage.stream().map(StagePO::getId).collect(Collectors.toList()));

            return;
        }

        List<StagePO> portStage = stageMapper.selectStageByServiceIdAndType(svcId, StageTypeEnum.SAFE_PORT.getType());
        if (CollUtil.isNotEmpty(portStage) && checkError(portStage)) {
            // 重试端口开通
            CreateCloudParam createCloudParam = new CreateCloudParam();
            createCloudParam.setSvcId(svcId);
            createCloudParam.setType(StageTypeEnum.CLOUD_RESOURCE.getType());
            createCloudParam.setUserName(TokenUtils.getUserName());
            createCloudParam.setCategory(microServicePO.getCategory());
            createCloudParam.setAppId(microServicePO.getApplicationId());
            createCloudParam.setArch(arch);
            createCloudParam.setAccountName(userChooseAccountName);
            createCloudParam.setCloudScope(cloudScope);
            cloudMgmtService.createCloudResource(createCloudParam);
            stageMapper.deleteStageByIds(portStage.stream().map(StagePO::getId).collect(Collectors.toList()));
            return;
        }

        List<StagePO> k8sStage = stageMapper.selectStageByServiceIdAndType(svcId, StageTypeEnum.K8S.getType());
        if (CollUtil.isNotEmpty(k8sStage) && checkError(k8sStage)) {
            // k8s重试
            CreateCloudParam createCloudParam = new CreateCloudParam();
            createCloudParam.setSvcId(svcId);
            createCloudParam.setType(StageTypeEnum.CLOUD_RESOURCE.getType());
            createCloudParam.setUserName(TokenUtils.getUserName());
            createCloudParam.setCategory(microServicePO.getCategory());
            createCloudParam.setAppId(microServicePO.getApplicationId());
            createCloudParam.setArch(arch);
            createCloudParam.setAccountName(userChooseAccountName);
            createCloudParam.setCloudScope(cloudScope);
            cloudMgmtService.createCloudResource(createCloudParam);
            stageMapper.deleteStageByIds(k8sStage.stream().map(StagePO::getId).collect(Collectors.toList()));
            return;
        }

        List<StagePO> distributeStage = stageMapper.selectStageByServiceIdAndType(svcId, StageTypeEnum.DISTRIBUTE.getType());
        if (CollUtil.isNotEmpty(distributeStage) && checkError(distributeStage)) {
            // 先删除当前的阶段信息
            // 在部署成功后，更新状态为启用时，会创建阶段4
            stageMapper.deleteStageByServiceIdAndType(svcId, StageTypeEnum.DISTRIBUTE.getType());
            // 部署重试
            // 跳过验证，直接部署
            // 需要确保此时集群信息已是最新

            // 部署集群信息在3-7的extraInfo中
            // ?是否需要加上algDeploy中的exist的集群
            List<StagePO> stagePOS = stageMapper.selectStagesByServiceId(svcId);
            StagePO stagePO = stagePOS.get(0);
            List<DeployClusterPO> deployClusterPOS = new ArrayList<>();
            List<Map<String, String>> extraInfos = stagePO.getExtraInfo();
            for (Map<String, String> extraInfo : extraInfos) {
                DeployClusterPO clusterPO = new DeployClusterPO();
                clusterPO.setId(IdUtil.simpleUUID());
                clusterPO.setClusterName(extraInfo.get("name"));
                clusterPO.setIp(extraInfo.get("ip"));
                clusterPO.setInnerIp(extraInfo.get("innerIp"));
                clusterPO.setProviderName(extraInfo.get("providerName"));
                clusterPO.setServiceId(svcId);
                clusterPO.setCreateTime(LocalDateTime.now(ZoneId.of("Asia/Shanghai")));

                deployClusterPOS.add(clusterPO);
            }

            OperateService operateService = SpringUtil.getBean(OperateService.class);

            DistributeParamV2 distributeParam = new DistributeParamV2();
            distributeParam.setServiceId(svcId);
            distributeParam.setClusters(deployClusterPOS);
            // 发送部署请求
            operateService.distributeV2(distributeParam);

        }
    }

    /**
     * 校验是否有错误
     *
     * @param stagePOS pos
     * @return 有错误：true；没有错误：false
     */
    private boolean checkError(List<StagePO> stagePOS) {
        for (StagePO po : stagePOS) {
            if (CharSequenceUtil.isNotEmpty(po.getError())) {
                return true;
            }
        }
        return false;
    }

    /**
     * 更新阶段状态
     *
     * @param updateStageParam 参数
     * @return 结果
     */
    @Override
    public boolean updateStage(UpdateStageParam updateStageParam) {
        if (updateStageParam.getSvcId().contains(IntelligentConstants.SEND_TO_CLOUD_MGT_PREFIX)) {
            String realSvcId = updateStageParam.getSvcId().replace(IntelligentConstants.SEND_TO_CLOUD_MGT_PREFIX, "");
            updateStageParam.setSvcId(realSvcId);
            LOGGER.warn("[intelligent]: set real svcId={}", realSvcId);
        }


        // 前3步由多云系统回调更新
        // 最后一步部署，需要手动更新
        StagePO stagePO = updateStageParam.toPO();
        // 不是云资源开通，先判断是否有前置的阶段完成
        if (stageService.isPreviousStageFinished(updateStageParam)) {
            return false;
        }

        String cloudScope = updateStageParam.getCloudScope();
        String arch = updateStageParam.getArch();
        // 如果存在后续阶段，需要删除
        // 1. 先查询当前阶段
        stageService.deleteNextStages(updateStageParam.getSvcId(), updateStageParam.getDetailType(), cloudScope, arch);

        StagePO oldStagePO = stageMapper.selectStageByServiceIdAndDetailType2(stagePO.getServiceId(), stagePO.getDetailType(), cloudScope, arch);
        if (Objects.nonNull(oldStagePO)) {
            // 存在之前的阶段信息，先删除
            LOGGER.warn("there has old stage, delete and reInsert, old={}", oldStagePO);
            stageMapper.deleteStageById(oldStagePO.getId());
        }
        // 插入阶段信息
        stageMapper.insertStage(stagePO);
        // 查询开始启动部署的阶段，3-7  3-9
        StageEnum2 startToDistributeEnum = StageEnum2.get37StageEnum2ByCloudTypeAndArch(updateStageParam.getCloudScope(), updateStageParam.getArch());

        if (startToDistributeEnum.getDetailType().equals(updateStageParam.getDetailType()) && CharSequenceUtil.isEmpty(updateStageParam.getErrorMsg())) {
            LOGGER.info("3-7 stage has finished, start to distribute");
            // 删除此时的4-1,4-2
            stageMapper.deleteStageByServiceIdAndType(updateStageParam.getSvcId(), StageTypeEnum.DISTRIBUTE.getType());
            // k8s纳管结束，开始真正部署
            // 部署阶段的二级分类信息插入，已在更新状态时插入
            insertStageWithK8s(updateStageParam);
        }

        if (CharSequenceUtil.isNotEmpty(stagePO.getError())) {
            LOGGER.info("stage({}) has error: {}, try to update status to failed", updateStageParam, updateStageParam.getErrorMsg());
            // 存在错误，则更新微服务状态为失败状态
            UpdateMicroServiceStatusParam param = new UpdateMicroServiceStatusParam();
            param.setId(updateStageParam.getSvcId());
            param.setStatus(String.valueOf(StrategyStatusEnum.FAILED.getStatus()));

            microSvcService.updateMicroServiceStatus(param);
        }

        return true;
    }


    /**
     * 插入k8s阶段
     *
     * @param updateStageParam 参数
     */
    private void insertStageWithK8s(UpdateStageParam updateStageParam) {
        ScheduleStrategyPO scheduleStrategyPO = scheduleStrategyMapper.selectByServiceId(updateStageParam.getSvcId());
        List<DeployClusterPO> deployClusterPOS = getDeployClusterPOS(updateStageParam, scheduleStrategyPO);

        // 选择用户选择的云商
        Set<String> clusterNames = deployClusterPOS.stream()
                .map(DeployClusterPO::getClusterName)
                .filter(Objects::nonNull).collect(Collectors.toSet());

        // 需要多云返回纳管虚机的集群信息
        List<Map<String, String>> extraInfos = updateStageParam.getExtraInfo();

        if (Objects.nonNull(extraInfos)) {
            for (Map<String, String> extraInfo : extraInfos) {
                String clusterName = extraInfo.get("name");
                // 过滤相同集群的信息
                if (Objects.isNull(clusterName) || clusterNames.contains(clusterName)) {
                    LOGGER.error("cluster name is null or cluster has been added, extraInfo={}", extraInfo);
                    continue;
                }
                DeployClusterPO clusterPO = new DeployClusterPO();
                clusterPO.setId(IdUtil.simpleUUID());
                clusterPO.setClusterName(clusterName);
                clusterPO.setIp(extraInfo.get("ip"));
                clusterPO.setInnerIp(extraInfo.get("innerIp"));
                clusterPO.setProviderName(extraInfo.get("providerName"));
                clusterPO.setServiceId(updateStageParam.getSvcId());
                clusterPO.setCreateTime(LocalDateTime.now(ZoneId.of("Asia/Shanghai")));
                clusterPO.setAccountName(scheduleStrategyPO.getUserChooseAccountName());

                if (clusterPO.getProviderName().equals(scheduleStrategyPO.getUserChooseCloud())) {
                    deployClusterPOS.add(clusterPO);
                }
            }
            // 在部署完成后，更新集群信息表
        }

        clusterNames = deployClusterPOS.stream().map(DeployClusterPO::getClusterName).collect(Collectors.toSet());

        // 先验证再部署
        ValidateParam validateParam = new ValidateParam();
        validateParam.setServiceId(updateStageParam.getSvcId());
        validateParam.setClusters(new ArrayList<>(clusterNames));
        // 使用数据库中的yaml
        validateParam.setYaml("");
        try {
            microSvcService.validate(validateParam);

            OperateService operateService = SpringUtil.getBean(OperateService.class);

            DistributeParamV2 distributeParam = new DistributeParamV2();
            distributeParam.setServiceId(updateStageParam.getSvcId());
            distributeParam.setClusters(deployClusterPOS);
            operateService.distributeV2(distributeParam);
        } catch (JsonException e) {
            // 异常，需要手动插入错误信息
            LOGGER.error("valid fail, svcId={}", updateStageParam.getSvcId());
            // 先删除原有的错误
            stageMapper.deleteStageByServiceIdAndType(updateStageParam.getSvcId(), StageTypeEnum.DISTRIBUTE.getType());
            StagePO errorStage = new StagePO();
            errorStage.setId(IdUtil.simpleUUID());
            errorStage.setServiceId(updateStageParam.getSvcId());
            errorStage.setError("验证部署失败");
            errorStage.setType(StageTypeEnum.DISTRIBUTE.getType());
            errorStage.setCloudScope(updateStageParam.getCloudScope());
            errorStage.setArch(updateStageParam.getArch());
            stageMapper.insertStage(errorStage);
            LOGGER.info("insert distribute error stage");

            LOGGER.info("stage({}) has error: {}, try to update status to failed", errorStage, errorStage.getError());
            // 存在错误，则更新微服务状态为失败状态
            UpdateMicroServiceStatusParam param = new UpdateMicroServiceStatusParam();
            param.setId(updateStageParam.getSvcId());
            param.setStatus(String.valueOf(StrategyStatusEnum.FAILED.getStatus()));
            // 更新状态
            microSvcService.updateMicroServiceStatus(param);
        }
    }

    private static List<DeployClusterPO> getDeployClusterPOS(UpdateStageParam updateStageParam, ScheduleStrategyPO scheduleStrategyPO) {
        // 用户选择的云商
        List<AlgDeployClusterInfo> userChooseCloud = scheduleStrategyPO.getAlgDeployClusterInfos().stream()
                .filter(algDeployClusterInfo -> scheduleStrategyPO.getUserChooseCloud().equals(algDeployClusterInfo.getProviderName()))
                .collect(Collectors.toList());
        // 转换成部署集群类
        List<DeployClusterPO> deployClusterPOS = userChooseCloud.stream()
                .filter(algDeployClusterInfo -> Objects.nonNull(algDeployClusterInfo.getName()))
                .map(algDeployClusterInfo -> {
                    DeployClusterPO po = algDeployClusterInfo.toDeployClusterPo();
                    po.setServiceId(updateStageParam.getSvcId());
                    return po;
                }).collect(Collectors.toList());
        return deployClusterPOS;
    }

    /**
     * 根据id删除阶段信息
     *
     * @param serviceId 服务id
     * @return 结果
     */
    @Override
    public boolean deleteStagesByServiceId(String serviceId) {
        return stageMapper.deleteStageByServiceId(serviceId) >= 1;
    }

    /**
     * 更新extraInfo
     *
     * @param svcId          微服务id
     * @param detailTypeEnum 具体阶段信息
     * @param extraInfos     extraInfos
     */
    @Override
    public void updateExtraInfo(String svcId, StageDetailTypeEnum detailTypeEnum,
                                List<Map<String, String>> extraInfos) {
        stageMapper.updateStageExtraInfoBySvcIdAndDetailType(svcId, detailTypeEnum.getDetailType(),
                extraInfos);
    }

    /**
     * 根据服务id和类型删除
     *
     * @param svcId 微服务id
     * @param type  类型
     */
    @Override
    public void deleteStagesBySvcIdAndType(String svcId, String type) {
        stageMapper.deleteStageByServiceIdAndType(svcId, type);
    }
}
